﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

using DigitalQuran.Text;

namespace DigitalQuran.Collection
{
    /// <summary>
    /// Collection of <see cref="DigitalQuran.Word"/>
    /// </summary>
    public class WordCollection : ReadOnlyCollection<Word>
    {
        /// <summary>
        /// Gets the total words in Qur'an
        /// </summary>
        public const int TotalWords = 77878;

        internal WordCollection() : this(0, TotalWords) { }

        int startIndex = 0;
        /// <summary>
        /// Creates new subset of <see cref="DigitalQuran.Collection.WordCollection"/>
        /// </summary>
        /// <param name="startIndex">Zero based index of first word</param>
        /// <param name="count">Total words to include in collection</param>
        public WordCollection(int startIndex, int count)
            : base(count)
        {
            this.startIndex = startIndex;
        }

        internal WordCollection(int startId, string[] words, Verse verse) : this(startId, words.Length)
        {
            int max = words.Length + startId;
            for (int i = startId; i < max; i++)
            {
                _Words[i] = new Word(i, verse);
                _WordsText[i] = words[i - startId];            
            }
        }

        public override Word this[int index]
        {
            get
            {
                if (index < Count)
                    return _Words[index + startIndex];
                else
                    throw new IndexOutOfRangeException();
            }
        }

        public override IEnumerator<Word> GetEnumerator()
        {
            for (int i = 0; i < Count; i++)
            {
                yield return _Words[i + startIndex];
            }
        }

        /// <summary>
        /// Gets the text for specifed text mode
        /// </summary>
        /// <param name="textMode">name of text mode</param>
        /// <returns>Returns the text</returns>
        public string ToString(string textMode)
        {
            var builder = new StringBuilder();
            foreach (var item in this)
            {
                builder.Append(item.ToString(textMode) + " ");
            }

            return builder.ToString();
        }

        /// <summary>
        /// Gets the frequency of specified letter in Words
        /// </summary>
        /// <param name="character"></param>
        /// <returns></returns>
        public int GetLetterFrequency(char character)
        {
            int result = 0;
            foreach (Word word in this)
                foreach (Letter letter in word.Letters)
                    if (letter.Character == character)
                        result++;

            return result;
        }

        int? _LettersCount = null;
        /// <summary>
        /// Gets the total number of letters
        /// </summary>
        public int LetterCount
        {
            get
            {
                if (!_LettersCount.HasValue)
                {
                    _LettersCount = 0;
                    foreach (var word in this)                    
                        _LettersCount += word.Letters.Count;                    
                }

                return _LettersCount.Value;
            }
        }

        /// <summary>
        /// Gets Letter at specific index in this Words Collection
        /// </summary>
        /// <param name="index">Index of letter</param>
        /// <returns>Returns Letter at specified index</returns>
        public Letter GetLetter(int index) {
            int i = 0;
            foreach (var w in this) {
                i += w.Letters.Count;
                if (i > index)
                    return w.Letters[index - i + w.Letters.Count];
            }
            return null;
        }

        static Word[] _Words = new Word[TotalWords];

        /// <summary>
        /// Gete the Active Text Mode
        /// </summary>
        public static string CurrentTextMode { get { return currentTextMode; } }

        static string currentTextMode = Book.OrignalTextMode;
        static string[] _WordsText = new string[TotalWords];

        static Dictionary<string, string[]> textTable = new Dictionary<string, string[]>();
        
        /// <summary>
        /// Loads the Specified Text mode
        /// </summary>
        /// <param name="textMode">Text mode to load</param>
        internal static void LoadTextMode(string textMode)
        {
            SimplificationRule rule;
            if (textMode != Book.OrignalTextMode && Client.Instance.Simplifier.SimplificationRules.TryGetValue(textMode, out rule))
            {
                /*
                if (textTable.Count == 2)
                    textTable.Remove(currentTextMode);
                 */

                AddSimplified(textMode, rule);
                currentTextMode = textMode;
            }
        }

        /// <summary>
        /// Simplifies the Orignal Text for according to specified <see cref="DigitalQuran.Text.SimplificationRule"/> and insert into the words text mode collection
        /// </summary>
        /// <param name="textMode">Name of text mode</param>
        /// <param name="rules">Simplification rules to simplify text</param>
        internal static void AddSimplified(string textMode, SimplificationRule rules)
        {
            if (textTable.ContainsKey(textMode)) return;

            string[] wordTextSet = new string[TotalWords];
            int id = 0;
            string text;

            if (textMode.Contains("Shadda"))
            {
                foreach (var wordText in _WordsText)
                {
                    StringBuilder str = new StringBuilder(wordText);
                    for (int i = 1; i < str.Length; i++)
                    {
                        if (str[i] == 'ّ') // replace shedda with previous letter                        
                            str[i] = str[i - 1];
                    }

                    text = str.ToString();

                    //TODO: Find Word Letters for 7:196
                    //// remove shedda in 7:196 as it is above a superscripted letter
                    //if ((verse.Chapter.Number == 7) && (verse.NumberInChapter == 196))
                    //{
                    //    text = text.Replace("ّۧ", "ۧ");
                    //}

                    foreach (var rule in rules)
                        text = text.Replace(rule.Key, rule.Value);

                    wordTextSet[id++] = text;
                }
            }
            else
            {
                foreach (var wordText in _WordsText)
                {
                    text = wordText;
                    foreach (var rule in rules)
                        text = text.Replace(rule.Key, rule.Value);

                    wordTextSet[id++] = text;
                }
            }

            textTable.Add(textMode, wordTextSet);
        }

        /// <summary>
        /// Get the text for specified word and text mode
        /// </summary>
        /// <param name="wordId">Zero based indoex of word</param>
        /// <param name="textMode">Name of text mode for which to get verse text</param>
        /// <returns>Returns the word text for specified text mode if text mode is loaded else returns <see cref="System.String.Empty"/> </returns>
        public static string GetText(int wordId, string textMode)
        {
            try
            {
                if (textMode == Book.OrignalTextMode)
                    return _WordsText[wordId];

                return textTable[textMode][wordId];
            }
            catch (Exception) { }

            return string.Empty;
        }

        /// <summary>
        /// Gets the text for specified text mode in range of words
        /// </summary>
        /// <param name="textMode">Text mode of which to get verse text</param>
        /// <param name="startId">Zero based index of word as Lower bound of range</param>
        /// <param name="endId">Zero based index of word as Upper bound of range</param>
        /// <returns>Returns the text if text mode is loaded else returns <see cref="System.String.Empty"/></returns>
        public static string GetTextRange(string textMode, int startId, int endId)
        {
            if (HasTextMode(textMode))
            {
                StringBuilder str = new StringBuilder();
                for (int i = startId; i < endId; i++)
                {
                    str.Append(GetText(i, textMode) + " ");
                }

                str.Remove(str.Length - 1, 1); // remove last space
                return str.ToString();
            }

            return string.Empty;
        }

        /// <summary>
        /// Check weather specific text mode exits
        /// </summary>
        /// <param name="textMode">name of text mode</param>
        /// <returns>Returns true if found else false</returns>
        public static bool HasTextMode(string textMode)
        {
            return Client.Instance.TextModes.Contains(textMode);
        }

        /// <summary>
        /// Gets the word at specified index
        /// </summary>
        /// <param name="index">Zero based index of word to get</param>
        /// <returns>Returns the Word</returns>
        public static Word GetWord(int index)
        {
            return _Words[index];
        }

        /// <summary>
        /// Loads the Uthmani Word Meanings
        /// </summary>
        internal static void LoadWordMeanings()
        {
            string filename = DigitalQuranDirectories.Data + "quran-uthmani-word-meanings.bin";

            if (File.Exists(filename))
            {
                using (var stream = new FileStream(filename, FileMode.Open))
                using (var reader = new BinaryReader(stream))
                {
                    for (int i = 0; i < TotalWords; i++)
                    {
                        _Words[i].Meaning = reader.ReadString();
                    }
                }
            }
        }

        /// <summary>
        /// Loads Uthmani Word Roots
        /// </summary>
        internal static void LoadWordRoots()
        {
            string filename = DigitalQuranDirectories.Data + "quran-uthmani-word-roots.bin";
            if (File.Exists(filename))
            {
                using (var stream = new FileStream(filename, FileMode.Open))
                using (var reader = new BinaryReader(stream))
                {
                    for (int i = 0, j; i < TotalWords; i++)
                    {
                        _Words[i].Roots = new string[reader.ReadInt32()];
                        for (j = 0; j < _Words[i].Roots.Length; j++)
                            _Words[i].Roots[j] = reader.ReadString();                        
                    }
                }
            }
        }

        internal static void LoadWordParts() {
            string filename = DigitalQuranDirectories.Data + "quran-uthmani-word-parts.bin";
            if (File.Exists(filename)) {
                using (var stream = new FileStream(filename, FileMode.Open))
                using (var reader = new BinaryReader(stream)) {
                    for (int i = 0; i < TotalWords; i++) {
                        _Words[i].Parts.ReadFromStream(_Words[i], reader);
                    }
                }
            }
        }

        public static void UpdateValues(Numerology.NumerologyEngine systems) {
            foreach (var word in _Words) {
                systems.CalculateValue(word);
            }
        }

        public static void UpdateLetterValues(Numerology.NumerologyEngine systems) {
            foreach (var word in _Words) {
                foreach (var letter in word.Letters) {
                    systems.CalculateValue(word.Verse, word.NumberInVerse - 1, letter.NumberInWord - 1);
                }
            }
        }
    }
}